<?php
define("QD_MODE_NONE", 0);
define("QD_MODE_VIEW", 1);
define("QD_MODE_EDIT", 2);
define("QD_MODE_ADD", 3);

define("QD_ACTION_DELETE", 0);
define("QD_ACTION_UPDATE", 1);
define("QD_ACTION_INSERT", 2);
//define("QD_MODE_ADD", 3);

/**
* DataForm.php
*
* Contains DataForm class definition
* @author  John Snook <jsnook@gmail.com>
* @version 2
* @copyright (c) Snook 2004
* @package QuickDataFiles
*/

/**
* DataForm
*
* This object is owned by DataTable, It allows a dynamically created edit or view page.
* @author  John Snook <jsnook@gmail.com>
* @version 2
* @copyright (c) Snook 2004
* @example /var/www/corp/IntraDox/Documents.php
* @todo Make formatting nicer.
* @package QuickData
*/
class DataForm {
	var $ConnectionString;
	var $DBTableName;
	var $PrimaryKeyName;
	var $PrimaryKeyValue;
	var $ParentKeyName;
	var $ParentKeyValue;
	var $QuickDataPath = NULL;  // Location of quickdata library, ie /IntraDox/QuickData
	var $DisplayMode = QD_MODE_NONE;
	var $CanEdit = FALSE;
	var $CanAdd = FALSE;
	var $CanDelete = TRUE;
	var $Fields; // array of Simple, OneToMany amd ManyToMany objects
	var $SequenceName = NULL;
	var $_buttons = null;
	
	/**
	* Constructor
	*/
	function DataForm($XMLFile, $TableID, $ObjectName)
	{
		$this->ID = $TableID;
		$this->ObjectName = $ObjectName;
		//print_r($xml);
		$this->_buttons = array('add'=>'Add New', 'edit'=>'Edit', 'update'=>'Update', 'delete'=>'Delete', 'cancel'=>'Cancel');
		$xml = simplexml_load_file($XMLFile);
		// Select our DataTable definition
		$Definition = $xml->xpath("//Form[@ID=\"$this->ID\"]");
		$Definition = $Definition[0];	
		// DataTable attributes
		foreach($Definition->attributes() as $key => $value)
		{
			if($value == "true" || $value == "false")
			{
				$value = ($value  == "true" ? true : false);
			}
			$php = '$this->' . "$key = '" . addslashes($value) . "';";
			eval($php);
		}
		$this->CanEdit = strtolower($ColumnDef['Visible'])  == "true" ? true : false;
		$Sortable = strtolower($ColumnDef['Sortable'])  == "true" ? true : false;

		// DataTable Fields
		foreach($Definition->xpath('//Field') as $field)
		{
			$fieldname = $field['LocalField'];
			$php = '$this->AddFieldObject(' . $fieldname . ', new ' . $field['Class'] . '($field));';
			//echo $php;
			eval($php);
			if(isset($field['InheritParent']))
			{
				$this->InheritParent($field['InheritParent']);
			}  
		}
	} 
	
	function HandleEvents($Request)
	{
		if (isset($Request["View"])) $this->SelectRecord($Request["PrimaryKeyValue"]);
		if (isset($Request["Edit"])) $this->SelectRecord($Request["PrimaryKeyValue"], QD_MODE_EDIT);
		if (isset($Request["Add"])) $this->SelectRecord(NULL, QD_MODE_ADD);
		if (isset($Request["Delete"]))
		{
			if ($Request["Delete"] > 0)
			{
				$this->PrimaryKeyValue = $Request["Delete"];
				$this->DeleteRecord();
			}
		}
		if (isset($Request["Cancel"])) $this->DisplayMode = QD_MODE_NONE;
		if (isset($Request["Update"]))  // we are coming from edit or add mode
		{
			foreach($this->Fields as $key => $Field){ //this is where we set all the values from the submitted form.
				if (($this->DisplayMode == QD_MODE_EDIT) || ($this->DisplayMode == QD_MODE_ADD))
				{
					if (! $Field instanceof FileUpload)
					{
						$value = $Request[$key];
						$this->Fields[$key]->Value = (strlen($value) == 0 ? NULL : $value);
					}
				}
			}

			$this->Update();
			$this->DisplayMode = QD_MODE_VIEW;
		}
	}
	/**
	* Deletes the currently selected record.
	* 5/17/2006 - and deletes any subrecords specified by fields collection.
	* -
	* If there is a file associated with a record, unlink it.
	*/
	function DeleteRecord()
	{
		if ($this->PrimaryKeyValue && $this->CanDelete)
		{
			$DB = new DBConnection($this->ConnectionString);
			// Handle file deletes first
			foreach($this->Fields as $key => $Field)
			{ 
				if ( $Field instanceof FileUpload && !is_null($Field->Value))
				{
					// Select count of all other records that have links to this file
					$SQL = "SELECT count(*) as count FROM {$this->DBTableName} WHERE {$Field->LocalField} = '{$Field->Value}' AND {$this->PrimaryKeyName} <> {$this->PrimaryKeyValue}";
					$Query = $DB->CreateQuery($SQL);
					$cnt = $Query->FetchRow();
					if ($cnt['count'] == 0) // only delete if nothing else points to it
					{
						if ( file_exists($Field->_UploadFile))
						{
							if (!unlink($Field->_UploadFile)) 
							{
								$out .= "Unable to delete file {$Field->_UploadFile}";
							}
						}
					}
				}
				if ($Field instanceof ManyToMany)
				{
					$SQL = "DELETE FROM {$Field->IntermediateTable} WHERE {$Field->IntermediateForeignKey} = {$this->PrimaryKeyValue} OR ";
					$SQL .= " {$Field->IntermediateLocalKey} = {$this->PrimaryKeyValue}";
					$Query = $DB->CreateQuery($SQL);
				}
			}

			$SQL = "DELETE FROM {$this->DBTableName} WHERE {$this->PrimaryKeyName} = {$this->PrimaryKeyValue}";
			$DB->CreateQuery($SQL);
			$this->PrimaryKeyValue = NULL;
			$this->DisplayMode = QD_MODE_NONE;
		}
	}
	
	/**
	* Selects a record to view edit or delete.  To add a record set $PrimaryKeyValue to NULL.
	*
	* @param string $PrimaryKeyValue the primary key value of the current record.
	* @param integer $DisplayMode 
	*/
	function SelectRecord($PrimaryKeyValue, $DisplayMode = QD_MODE_VIEW)
	{
		$this->PrimaryKeyValue = $PrimaryKeyValue;  // Pass NULL to add
		$this->DisplayMode = $DisplayMode;
	}
	
	/**
	* If we have ParentKeyName and ParentKeyValue set, this function will cause any list fields to filter by this criteria.
	*/
	function InheritParent($key)
	{
		if (!is_null($this->ParentKeyName) && !is_null($this->ParentKeyValue)){
			if ($this->Fields[$key]->Type != "Simple"){
				// since this has to be explicitly set, leave this code here
				$this->Fields[$key]->ParentKeyName = $this->ParentKeyName;
				$this->Fields[$key]->ParentKeyValue = $this->ParentKeyValue;
			}
		}
	}
	

	/**
	* Adds an object to the that subclasses a Field to the $Fields collection.
	*
	* @todo Move this to a factory class
	*/
	function AddFieldObject($key, $FieldObject)
	{
		if ( is_array($this->Fields) && array_key_exists($key, $this->Fields)) die("Error adding new Fields Object: $key exists in Fields array.");
		$this->Fields[$key] = $FieldObject;
		$this->Fields[$key]->Key = $key;
		$this->Fields[$key]->Parent = &$this;
	}

	/**
	* Renders HTML for DataForm object based on $this->DisplayMode
	* 
	* Each field in the $Fields array is responsible for rendering its own add/edit control.
	* - QD_MODE_EDIT <br>
	*   Displays Caption and Read-only value
	* - QD_MODE_EDIT <br>
	*   Displays $Caption and input control with the same name as the $LocalField
	*   Will also include a hidden field named PrimaryKeyValue to hold the row id
	* - QD_MODE_ADD <br>
	*   Displays $Caption and empty input control with the same name as the $LocalField
	*   As of 2-29-05, I'm delegating responsibility for knowing how to display to the individual field (CanAdd and CanEdit)
	* - QD_MODE_SEARCH <br>
	*   Should change the edit/add button to search
	*   As of 5-09-05, I'm delegating responsibility for knowing how to display to the individual field (CanAdd and CanEdit)
	*/
	function GetFormHTML()
	{
		$DB = new DBConnection($this->ConnectionString);
		$out = '';
		if (count($this->Fields) == 0) return get_class($this) . " requires at least 1 field.";

		if ($this->DisplayMode != QD_MODE_ADD)
		{
			$Query = $DB->CreateQuery($this->GetQuery());
			$row = $Query->FetchRow();
			$Query->Close();
		}
		
/*		$out .= "\n<!-- WARNING: The following table was generated by John Snooks' Excellent " . get_class($this) . " Class! -->\n";
		$out .= "<script language=\"javascript\" src=\"{$this->QuickDataPath}/javascript/calendarlib.js\"></script>\n";
*/
		$out .= "<form class=\"DataForm\" action=\"\" method=\"POST\" enctype=\"multipart/form-data\"  name=\"QuickData\">\n";

		$out .= "<table class=\"DataFormTable\" cellpadding=\"4\" cellspacing=\"0\" border=\"0\" width=\"100%\">\n";
		foreach($this->Fields as $key => $Field)
		{
			//	As of 2-29-05, I'm delegating responsibility for knowing how to display to the individual field
			$out .= $Field->Render($row, $this->DisplayMode);
		}

		//edit update cancel delete buttons rendered here
		$out .= "\t<tr>\n\t\t<td>&nbsp;</td>\n";
	    $out .= "\t\t<td class=\"buttons\">\n";
		if ($this->CanEdit)
		{
			switch ($this->DisplayMode)
			{
				case QD_MODE_VIEW:
					$out .= "<input name=\"Edit\" type=\"hidden\" value=\"Edit\"> ";
					$out .= "<a href=\"#\" onClick=\"document.forms['QuickData'].submit();\" >{$this->_buttons['edit']}</a> ";
					// Delete button
					if ( $this->CanDelete )
					{

						$out .= "<a href=\"#\" onClick=\"confirmDelete();\">{$this->_buttons['delete']}</a>";
					}
					break;
				case QD_MODE_EDIT:
					$out .= "<input name=\"Update\" type=\"hidden\" value=\"Update\"> ";
					$out .= "<a href=\"javascript:{$this->ObjectName}.submit({QD_ACTION_UPDATE});\">{$this->_buttons['update']}</a>";
					break;
				case QD_MODE_ADD:
					$out .= "<input name=\"Update\" type=\"hidden\" value=\"Add New\"> ";
					$out .= "<a href=\"javascript:{$this->ObjectName}.submit({QD_ACTION_INSERT});\">{$this->_buttons['add']}</a>";
					break;
			}
		}
		$out .= "<a href=\"?Cancel=TRUE\">{$this->_buttons['cancel']}</a>";
		$out .= "<input name=\"PrimaryKeyValue\" type=\"hidden\" value=\"" . $this->PrimaryKeyValue . "\">";
	    $out .= "\t\t</td>\n";
		$out .= "\t</tr>\n";
		$out .= "</table></form>\n";
		return $out;
	}
	
	/**
	*@return  string  "SELECT" statement for the $Fields array in DataForm
	*/
	function GetQuery()
	{
		$SQL = "SELECT ";
		foreach($this->Fields as $key => $Field)
		{
			if ( $Field->CanQuery )
			{
				$SQL .= $Field->LocalField . ", ";
			}
		}
		$SQL = substr($SQL, 0, strlen($SQL) - 1); //strip last comma
		$SQL .= " FROM " . $this->DBTableName . " WHERE " . $this->PrimaryKeyName . " = " . $this->PrimaryKeyValue;
		return $SQL;
	}
	
	/**
	* Updates existing records or adds
	*
	* - QD_MODE_EDIT <br> 
	*   Loops through $Fields collection and calls the Update method.
	* - QD_MODE_ADD <br> 
	*   Builds and executes INSERT INTO sql statement by looping through $Fields collection.
	*
	*
	* @uses Simple::Update()
	* @uses FileUpload::HandleUpload()
	*/
	function Update()
	{ 
		$DB = new DBConnection($this->ConnectionString);
		switch($this->DisplayMode)
		{
			case QD_MODE_EDIT:
				// each field is responsible for updating itself
				foreach($this->Fields as $key => $Field)
				{	
					$this->Fields[$key]->Update();
				}
				break;
			case QD_MODE_ADD:
				// Handle file uploads first
				foreach($this->Fields as $key => $Field)
				{
					// without this, we can't add files when creating records
					if ( $Field instanceof FileUpload )
					{
						$this->Fields[$key]->HandleUpload();
					}
				}

				$SQL = "INSERT INTO " . $this->DBTableName . "(";
				// Field List
				foreach($this->Fields as $key => $Field) 
				{
					if ( $Field->CanInsert )
					{
						$SQL .= $Field->LocalField . ",";
					}
				}
				if ($this->ParentKeyName && (!is_null($this->ParentKeyValue))) $SQL .= $this->ParentKeyName . ",";
				$SQL = substr($SQL,0, strlen($SQL)-1);
				
				// Values List
				$SQL .= ") VALUES (";
				foreach($this->Fields as $key => $Field)
				{ 
					if ( $Field->CanInsert )
					{
						$SQL .= $Field->GetSQLReadyValue() . ",";
					}
				}
				if ($this->ParentKeyName && (!is_null($this->ParentKeyValue))) 
				{
					$SQL .= $this->ParentKeyValue .",";
				}
				
				$SQL = substr($SQL,0, strlen($SQL)-1);
				$SQL .= ")";
//				echo "$SQL<br>";
				$Query = $DB->CreateQuery($SQL);
				$Query->Close(); 


				if (!is_null($this->SequenceName))
				{
					$SQL = "SELECT currval('{$this->SequenceName}') as PrimaryKeyValue";
				}
				else
				{
					$SQL = "SELECT currval('" . $this->DBTableName . "_" . $this->PrimaryKeyName . "_seq') as PrimaryKeyValue";
				}
				$Query = $DB->CreateQuery($SQL);
				$row = $Query->FetchRow();
				$this->PrimaryKeyValue = $row['primarykeyvalue'];
				$Query->Close();
				// Now that we have a primary key, we can update any join table fields (qdf_manytomany)
				foreach($this->Fields as $key => $Field)
				{ 
//					if ( !(get_class($Field) == 'qdf_manytomany') || (is_subclass_of($Field,'qdf_manytomany')) )
					if ( !$Field->CanInsert )
					{
						$this->Fields[$key]->Update();
					}
				}
				break;
		}
	}
} //DataForm

?>